/*
 * Copyright (c) 2011.
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 *
 */
package org.graphoman.math;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import org.graphoman.model.Model;
import org.graphoman.model.State;
import org.graphoman.model.Transition;
import org.jfree.data.xy.XYSeriesCollection;

import Jama.EigenvalueDecomposition;
import Jama.Matrix;

/**
 * @author sorseg
 */
public class Calculator {
    /**
     * @param model - model with states and transitions
     * @return matrix of transitions between states
     */
    public static Matrix calculateTransitionMatrix(Model model) {
        Vector<State> states = new Vector<State>(model.states);
        Map<State, Integer> stateIntegerMap = new HashMap<State, Integer>();
        for (int i = 0; i < states.size(); i++) {
            stateIntegerMap.put(states.get(i), i);
        }
        Matrix lambda = new Matrix(states.size(), states.size());
        for (Transition transition : model.transitions) {
            double value = lambda.get(
                    stateIntegerMap.get(transition.from),
                    stateIntegerMap.get(transition.from)
                    );
            lambda.set(
                    stateIntegerMap.get(transition.from),
                    stateIntegerMap.get(transition.from),
                    value - Double.parseDouble(transition.intensity)
                    );
            value = lambda.get(
                    stateIntegerMap.get(transition.from),
                    stateIntegerMap.get(transition.to)
                    );
            lambda.set(
                    stateIntegerMap.get(transition.from),
                    stateIntegerMap.get(transition.to),
                    value + Double.parseDouble(transition.intensity)
                    );
        }

        StringBuilder sb = new StringBuilder();
        sb.append("[\n");
        for (double[] obj : lambda.transpose().getArray()) {
            for (double o : obj) {
                sb.append(o);
                sb.append(" ");
            }
            sb.append(";\n");
        }
        sb.append("]");
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        StringSelection ss = new StringSelection(sb.toString());
        clipboard.setContents(ss, new ClipboardOwner() {

            public void lostOwnership(Clipboard arg0, Transferable arg1) {
                // TODO Auto-generated method stub

            }
        });
        return lambda;

    }

    /**
     * Надо найти P(t) из уравнения P'(t) = AP(t), где P(t) - вектор состояний, A - матрица переходов.
     * Решением будет функция вида exp(A * (t-t0) ) * P0.
     * Матричная экспонента вычисляется так:
     * exp(A) = E + A + A^2 / 2! + A^3 / 3! + ...  и так до seriesSteps
     *
     * @param states           - список состояний, с нумерацией, соответствующей матрице интенсивностей
     * @param initialState     - начальное состояние
     * @param transitionMatrix - матрица интенсивностей
     * @param time             - начало отсчёта
     * @param endTime          - конец отсчёта
     * @param step             - шаг
     * @param seriesSteps      - количество слагаемых в матричной экспоненте
     * @return
     */
    public static XYSeriesCollection calculateProcess(
            Vector<State> states,
            int initialState,
            Matrix transitionMatrix,
            double time, double endTime, double step, int seriesSteps) {
        // предполагаем, что матрица квадратная
        int n = transitionMatrix.getColumnDimension();

        // проверка
        if (n != states.size()) {
            //throw new IllegalArgumentException();
        }

        // создаём начальный вектор
        //        Matrix p0 = new Matrix(1, n);
        //        p0.set(0, initialState, 1);

        // результат
        //XYSeriesCollection result = new XYSeriesCollection();

        // процесс трудоёмкий...
        //        for (; time < endTime; time += step) {
        //
        //            Matrix exponent = Matrix.identity(n, n);
        //            Matrix lambdaT = new Matrix(transitionMatrix.getArray());
        //            lambdaT.times(time);
        //            Matrix addition = Matrix.identity(n, n);
        //
        //            for (int i = 0; i < seriesSteps; i++) {
        //                addition.arrayTimesEquals(lambdaT);
        //                addition.times(1.0 / i);
        //
        //                exponent.plusEquals(addition);
        //            }
        //            p0.times(exponent);
        //
        //            EigenvalueDecomposition nnn = transitionMatrix.eig();
        //            Matrix V = nnn.getV();
        //            Matrix D = nnn.getD();
        //            System.out.println(V);
        //            System.out.println(D);
        //
        //        }
        // создаём ряд (линию на графике, поведение состояния)
        //XYSeries xySeries = new XYSeries(states.get(stateIndex).name);

        //result.addSeries(xySeries);

        return null;
    }

    private static Matrix matrixExponential(double t, Matrix A) {
        EigenvalueDecomposition n = A.eig();
        System.out.println(n);

        return null;
    }
}
